/*
Copyright (c) 2017 Tony Pottier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@file wifi_manager.h
@author Tony Pottier
@brief 定义esp32连接到wifi /扫描wifis所需的所有功能包含freeRTOS任务和所有必要的支持

@see https://idyl.io
@see https://github.com/tonyp7/esp32-wifi-manager
*/

#ifndef WIFI_MANAGER_H_INCLUDED
#define WIFI_MANAGER_H_INCLUDED


#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief If WIFI_MANAGER_DEBUG 被定义为, 其他调试信息将发送到标准输出。
 */
// #define WIFI_MANAGER_DEBUG	1

/**
 * @brief 定义SSID名称的最大大小。 32是IEEE标准。
 * @warning limit也在wifi_config_t中进行了硬编码。永远不要扩展此值。
 */
#define MAX_SSID_SIZE		32

/**
 * @brief 定义WPA2密钥的最大大小。 64是IEEE标准。
 * @warning limit也在wifi_config_t中进行了硬编码。永远不要扩展此值。
 */
#define MAX_PASSWORD_SIZE	64


/**
 * @brief 定义可扫描的最大访问点数。
 *
 * 为了节省内存并避免令人讨厌的内存错误，我们可以限制在wifi扫描中检测到的AP数量。
 */
#define MAX_AP_NUM 			15


/** @brief 将auth模式定义为访问点值必须是wifi_auth_mode_t类型
 *  @see esp_wifi_types.h
 */
#define AP_AUTHMODE 		WIFI_AUTH_WPA2_PSK

/** @brief 定义接入点的可见性。 0：可见AP。 1：隐藏 */
#define DEFAULT_AP_SSID_HIDDEN 		0

/** @brief 定义访问AP点的名称。 */
#define DEFAULT_AP_SSID 			"esp32"

/** @brief 定义访问AP点的密码。
 *	@warning 对于开放访问点，如果您想要详细但浪费一个字节，则密码必须是空字符串“”或“\ 0”。
 */
#define DEFAULT_AP_PASSWORD 		"esp32pwd"

/** @brief 定义接入点的带宽。
 *  Value: WIFI_BW_HT20 for 20 MHz  or  WIFI_BW_HT40 for 40 MHz
 *  20 MHz 尽量减少信道干扰，但不适合具有高数据速度的应用程序
 */
#define DEFAULT_AP_BANDWIDTH 			WIFI_BW_HT20

/** @brief 定义接入点的频道。
 *  通道选择仅在未连接到其他AP时有效。
 *  使用最少信道干扰的良好做法
 *  For 20 MHz: 1, 6 or 11 in USA and 1, 5, 9 or 13 in most parts of the world
 *  For 40 MHz: 3 in USA and 3 or 11 in most parts of the world
 */
#define DEFAULT_AP_CHANNEL 			5

/** @brief 定义访问点的最大客户端数。 */
#define AP_MAX_CONNECTIONS 	4

/** @brief 定义接入点的信标间隔。建议默认值为100毫秒。 */
#define AP_BEACON_INTERVAL 	100

/** @brief 定义当连接到另一个AP时esp32是否应运行AP + STA。
 *  Value: 0 将始终打开自己的AP（APSTA模式）
 *  Value: 1 连接到另一个AP时将关闭自己的AP（连接时仅限STA模式）
 *  连接到另一个AP时关闭自身AP可以最大限度地减少信道干扰并提高吞吐量
 */
#define DEFAULT_STA_ONLY 			1

/** @brief 定义是否应启用wifi节电功能。
 *  Value: WIFI_PS_NONE 全功率（wifi调制解调器始终打开）
 *  Value: WIFI_PS_MODEM 用于省电（wifi调制解调器定期睡眠）
 *  Note: 省电仅在仅STA模式下有效
 */
#define DEFAULT_STA_POWER_SAVE 			WIFI_PS_MODEM

/**
 * @brief 定义访问点的JSON表示的最大长度（以字节为单位）。
 *
 *  最大ap字符串长度，满32个字符ssid：75 + \\ n + \ 0 = 77 \ n
 *	示例：{“ssid”：“abcdefghijklmnopqrstuvwxyz012345”，“chan”：12，“rssi”： -  100，“auth”：4}，\ n
 *  BUT: 我们需要逃避JSON。想象一个ssid充满了\“？所以它多了32个字节，因此77 + 32 = 99. \ n
 *这是一个边缘案例，但我认为我们不应该因为灾难性的方式崩溃
 *有人决定有一个有趣的wifi名称。
 */
#define JSON_ONE_APP_SIZE 99

/**
 * @brief 定义IP信息的JSON表示的最大长度（以字节为单位）
 *假设所有ips都是4 * 3位数，并且ssid中的所有字符都需要进行转义。
 * example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0}
 */
#define JSON_IP_INFO_SIZE 150



typedef enum update_reason_code_t {
	UPDATE_CONNECTION_OK = 0,
	UPDATE_FAILED_ATTEMPT = 1,
	UPDATE_USER_DISCONNECT = 2,
	UPDATE_LOST_CONNECTION = 3
}update_reason_code_t;

/**
 * 正在使用的实际WiFi设置
 */
struct wifi_settings_t{
	uint8_t ap_ssid[MAX_SSID_SIZE];
	uint8_t ap_pwd[MAX_PASSWORD_SIZE];
	uint8_t ap_channel;
	uint8_t ap_ssid_hidden;
	wifi_bandwidth_t ap_bandwidth;
	bool sta_only;
	wifi_ps_type_t sta_power_save;
	bool sta_static_ip;
	tcpip_adapter_ip_info_t sta_static_ip_config;
};
extern struct wifi_settings_t wifi_settings;


/**
 * 释放wifi_manager分配的所有内存并终止任务。
 */
void wifi_manager_destroy();

/**
 * 将AP扫描列表过滤为唯一的SSID
 */
void filter_unique( wifi_ap_record_t * aplist, uint16_t * ap_num);

/**
 * wifi_manager的主要任务
 */
void wifi_manager( void * pvParameters );


char* wifi_manager_get_ap_list_json();
char* wifi_manager_get_ip_info_json();




/**
 * @brief 将当前的STA wifi配置保存到闪存RAM存储。
 */
esp_err_t wifi_manager_save_sta_config();

/**
 * @brief 在flash ram存储中获取以前的STA wifi配置。
 * @return 如果找到以前保存的配置，则为true，否则为false。
 */
bool wifi_manager_fetch_wifi_sta_config();

wifi_config_t* wifi_manager_get_wifi_sta_config();


/**
 * @brief 标准的wifi事件管理器。
 * 未返回数据（发送数据超时）。
 * SYSTEM_EVENT_AP_START
 * SYSTEM_EVENT_AP_STACONNECTED
 * SYSTEM_EVENT_AP_STADISCONNECTED
 * SYSTEM_EVENT_STA_START
 * SYSTEM_EVENT_STA_GOT_IP
 * SYSTEM_EVENT_STA_DISCONNECTED
 */
esp_err_t wifi_manager_event_handler(void *ctx, system_event_t *event);


/**
 * @brief 请求连接到将在主任务线程中处理的访问点。
 */
void wifi_manager_connect_async();

/**
 * @brief 请求wifi扫描
 */
void wifi_manager_scan_async();

/**
 * @brief 请求断开连接并忘记访问点。
 */
void wifi_manager_disconnect_async();

/**
 * @brief 尝试访问json缓冲区互斥锁。
 *
 * HTTP服务器可以尝试访问json以服务客户端，而wifi管理器线程可以尝试
 *更新它。这两个任务通过互斥锁同步。
 *
 * 接入点列表json和连接状态json都使用互斥锁。\ n
 * 这两种资源在技术上应该有自己的互斥锁，但是我们失去了一些保存的灵活性关于记忆。
 *
 * 这是一个围绕freeRTOS函数xSemaphoreTake的简单包装器。
 *
 * @param xTicksToWait 等待信号量变为可用的时间。
 * @return true in success, false otherwise.
 */
bool wifi_manager_lock_json_buffer(TickType_t xTicksToWait);

/**
 * @brief 释放json缓冲区互斥锁。
 */
void wifi_manager_unlock_json_buffer();

/**
 * @brief 生成连接状态json：ssid和IP地址。
 * @note 这不是线程安全的，只有在wifi_manager_lock_json_buffer调用成功时才应调用。
 */
void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code);
/**
 * @brief 清除连接状态json。
 * @note 这不是线程安全的，只有在wifi_manager_lock_json_buffer调用成功时才应调用。
 */
void wifi_manager_clear_ip_info_json();

/**
 * @brief 在wifi扫描之后生成接入点列表。
 * @note 这不是线程安全的，只有在wifi_manager_lock_json_buffer调用成功时才应调用。
 */
void wifi_manager_generate_acess_points_json();

/**
 * @brief 清除接入点列表。
 * @note 这不是线程安全的，只有在wifi_manager_lock_json_buffer调用成功时才应调用。
 */
void wifi_manager_clear_access_points_json();

#ifdef __cplusplus
}
#endif

#endif /* WIFI_MANAGER_H_INCLUDED */
